#!/usr/bin/env escript
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-

-mode(compile).

-define(DEFAULT_FILEPATH, "aeternity_db.export").

main(Args) ->
    {RestArgs, TargetNode} = process_node_args(Args, [], undefined),
    {Help, FilePath} = process_args(RestArgs),

    maybe_show_help(Help),

    connect_node(TargetNode),

    do_main(TargetNode, FilePath).

usage() ->
    io:format("Usage: export_chain [help | FILE_PATH]~n", []).

error_exit(Fmt, FmtArgs) ->
    io:format(Fmt ++ "~n", FmtArgs),
    halt(1).

do_main(TargetNode, FilePath) ->
    case filelib:is_regular(FilePath) of
        true ->
            error_exit("File ~s already exists", [FilePath]);
        _ ->
            mkdir_p(filename:dirname(FilePath)),
            case rpc:call(TargetNode, aeu_export, to_disklog, [FilePath]) of
                {ok, BlockCount} ->
                    io:format("~i blocks exported to ~s~n", [BlockCount, FilePath]),
                    halt(0);
                {error, Reason} ->
                    error_exit("Error while exporting to ~s: ~p", [FilePath, Reason])
            end
    end.

mkdir_p(Path) ->
    case filelib:is_dir(Path) of
        true ->
            ok;
        false ->
            case file:make_dir(Path) of
                ok ->
                    ok;
                {error, enoent} ->
                    mkdir_p(filename:dirname(Path)),
                    mkdir_p(Path);
                {error, Err} ->
                    error_exit("Can't create directory ~s: ~p", [Path, Err])
            end
    end.

process_args([]) ->
    {false, ?DEFAULT_FILEPATH};
process_args(["help" | _]) ->
    {true, ?DEFAULT_FILEPATH};
process_args([FilePath | _]) ->
    {false, FilePath}.


%%
%% Common Helper Functions
%%

maybe_show_help(true) ->
    usage(),
    halt(0);
maybe_show_help(false) ->
    ok.

process_node_args([], Acc, TargetNode) ->
    {lists:reverse(Acc), TargetNode};
process_node_args(["-setcookie", Cookie | Rest], Acc, TargetNode) ->
    erlang:set_cookie(node(), list_to_atom(Cookie)),
    process_node_args(Rest, Acc, TargetNode);
process_node_args(["-name", TargetName | Rest], Acc, _) ->
    ThisNode = append_node_suffix(TargetName, "_util_"),
    {ok, _} = net_kernel:start([ThisNode, longnames]),
    process_node_args(Rest, Acc, nodename(TargetName));
process_node_args(["-sname", TargetName | Rest], Acc, _) ->
    ThisNode = append_node_suffix(TargetName, "_util_"),
    {ok, _} = net_kernel:start([ThisNode, shortnames]),
    process_node_args(Rest, Acc, nodename(TargetName));
process_node_args([Arg | Rest], Acc, Opts) ->
    process_node_args(Rest, [Arg | Acc], Opts).

append_node_suffix(Name, Suffix) ->
    case re:split(Name, "@", [{return, list}, unicode]) of
        [Node, Host] ->
            list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host]));
        [Node] ->
            list_to_atom(lists:concat([Node, Suffix, os:getpid()]))
    end.

nodename(Name) ->
    case re:split(Name, "@", [{return, list}, unicode]) of
        [_Node, _Host] ->
            list_to_atom(Name);
        [Node] ->
            [_, Host] = re:split(atom_to_list(node()), "@", [{return, list}, unicode]),
            list_to_atom(lists:concat([Node, "@", Host]))
    end.

connect_node(TargetNode) ->
    case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of
        {true, pong} ->
            ok;
        {_, pang} ->
            io:format("Node ~p not responding to pings.~n", [TargetNode]),
            halt(1)
    end.
